<?php

include_once "wxBizDataCrypt.php";
include_once "xpay_config.php";

class XiaoChengXu {
    const ERRCODE_LOGIN_EXPIRE = -1000; // 登录过期了,需要重新授权
    const ERRMSG_LOGIN_EXPIRE = '登录过期了,需要重新授权';
    const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀
    const JSCODE_TO_SESSION = '/sns/jscode2session?';

    public function __construct($options = array()) {
        if (isset($options['appid'])) {
            $this->appid = $options['appid'];
        }

        if (isset($options['appsecret'])) {
            $this->appsecret = $options['appsecret'];
        }
    }

    public function getUserInfo($code, $encryptedData, $iv) {
        if (!$code) return false;
        if (!$encryptedData) return false;
        if (!$iv) return false;

        $rs_code = $this->jscode2session($code);

        if (!$rs_code) return false;
        $sessionKey = $rs_code['session_key'];
        $openid = $rs_code['openid'];

        $pc = new WXBizDataCrypt($this->appid, $sessionKey);
        $errCode = $pc->decryptData($encryptedData, $iv, $data );

        if ($errCode == 0) {
            $rs_data = json_decode($data, true);
            if ($openid != $rs_data['openId']) return false; // 对比openid

            $rs_data['session_key'] = $sessionKey;

            return $rs_data;
        } else {
            return false;
        }

        /*
            {
                "openId": "oGZUI0egBJY1zhBYw2KhdUfwVJJE",
                "nickName": "Band",
                "gender": 1,
                "language": "zh_CN",
                "city": "Guangzhou",
                "province": "Guangdong",
                "country": "CN",
                "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0",
                "unionId": "ocMvos6NjeKLIBqg5Mr9QjxrP1FA",
                "watermark": {
                    "timestamp": 1477314187,
                    "appid": "wx4f4bc4dec97d474b"
                }
            }
        */
    }

    private function jscode2session($js_code) {
        if (!$js_code) return false;
        $result = $this->http_get(self::API_BASE_URL_PREFIX.self::JSCODE_TO_SESSION.'appid='.$this->appid.'&secret='.$this->appsecret.'&js_code='.$js_code.'&grant_type=authorization_code');

        if ($result) {
            $json = json_decode($result,true);
            if (!$json || !empty($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            return $json;
        }
        return false;
    }

    private function http_get($url){
        $oCurl = curl_init();
        if(stripos($url,"https://")!==FALSE){
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if(intval($aStatus["http_code"])==200){
            return $sContent;
        }else{
            return false;
        }
    }

    private function http_post($url,$param,$post_file=false){
        $oCurl = curl_init();
        if(stripos($url,"https://")!==FALSE){
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        if (PHP_VERSION_ID >= 50500 && class_exists('\CURLFile')) {
            $is_curlFile = true;
        } else {
            $is_curlFile = false;
            if (defined('CURLOPT_SAFE_UPLOAD')) {
                curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false);
            }
        }
        if (is_string($param)) {
            $strPOST = $param;
        }elseif($post_file) {
            if($is_curlFile) {
                foreach ($param as $key => $val) {
                    if (substr($val, 0, 1) == '@') {
                        $param[$key] = new \CURLFile(realpath(substr($val,1)));
                    }
                }
            }
            $strPOST = $param;
        } else {
            $aPOST = array();
            foreach($param as $key=>$val){
                $aPOST[] = $key."=".urlencode($val);
            }
            $strPOST =  join("&", $aPOST);
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt($oCurl, CURLOPT_POST,true);
        curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
        $sContent = curl_exec($oCurl);

        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if(intval($aStatus["http_code"])==200){
            return $sContent;
        }else{
            return false;
        }
    }

    private function xmlSafeStr($str) {
        return '<![CDATA['.preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/",'',$str).']]>';
    }

    private function data_to_xml($data) {
        $xml = '';
        foreach ($data as $key => $val) {
            is_numeric($key) && $key = "item id=\"$key\"";
            $xml    .=  "<$key>";
            $xml    .=  ( is_array($val) || is_object($val)) ? $this->data_to_xml($val)  : $this->xmlSafeStr($val);
            list($key, ) = explode(' ', $key);
            $xml    .=  "</$key>";
        }
        return $xml;
    }

    public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {
        if(is_array($attr)){
            $_attr = array();
            foreach ($attr as $key => $value) {
                $_attr[] = "{$key}=\"{$value}\"";
            }
            $attr = implode(' ', $_attr);
        }
        $attr   = trim($attr);
        $attr   = empty($attr) ? '' : " {$attr}";
        $xml   = "<{$root}{$attr}>";
        $xml   .= $this->data_to_xml($data, $item, $id);
        $xml   .= "</{$root}>";
        return $xml;
    }

    public function xmlToArray($xml){
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring),true);
        return $val;
    }

    private function generateNonceStr($length=32){
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for($i = 0; $i < $length; $i++)
        {
            $str .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $str;
    }

    private function getSignature($arrdata) {
        global $XPAY_CONFIG;

        $arrdata = array_filter($arrdata);
        ksort($arrdata);

        $paramstring = "";
        foreach ($arrdata as $key => $value) {
            if (strlen($paramstring) == 0) {
                $paramstring .= $key . "=" . $value;
            } else {
                $paramstring .= "&" . $key . "=" . $value;
            }
        }
        $paramstring .= "&key=" . $XPAY_CONFIG['SIGN_KEY'];
        $Sign = MD5($paramstring);
        $Sign = strtoupper($Sign);

        return $Sign;
    }

    public function checkSign($arrdata) {
        global $XPAY_CONFIG;

        ksort($arrdata);

        $paramstring = "";
        foreach ($arrdata as $key => $value) {
            if ($key != 'sign') {
                if (strlen($paramstring) == 0) {
                    $paramstring .= $key . "=" . $value;
                } else {
                    $paramstring .= "&" . $key . "=" . $value;
                }
            }
        }
        $paramstring .= "&key=" . $XPAY_CONFIG['SIGN_KEY'];
        $Sign = MD5($paramstring);
        $Sign = strtoupper($Sign);

        if ($Sign == $arrdata['sign']) {
            return true;
        }

        return false;
    }

    public function getOrder($option) {
        global $XPAY_CONFIG;

        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

        $params = array(
            'appid' => $XPAY_CONFIG['APP_ID'],
            'mch_id' => $XPAY_CONFIG['MCH_ID'],
            'nonce_str' => $this->generateNonceStr(),
            'sign_type' => 'MD5',
            'body' => $option['serviceName'],
            'out_trade_no' => $option['out_trade_no'],
            'fee_type' => 'CNY',
            'total_fee' => $option['money'], // 支付金额,单位分
            'notify_url' => $XPAY_CONFIG['NOTIFY_URL'],
            'trade_type' => 'JSAPI',
            'openid' => $option['openid'],
        );

        $params['sign'] = $this->getSignature($params);

        $xml = $this->xml_encode($params);
        $ret = $this->http_post($url, $xml);
        if ($ret) {
            $arr = $this->xmlToArray($ret);
            if ('SUCCESS' == $arr['return_code'] // 通信成功
                && 'SUCCESS' == $arr['result_code']) { // 业务成功
                $pay_info = array(
                    'appId' => $XPAY_CONFIG['APP_ID'],
                    'timeStamp' => strval(TIMESTAMP),
                    'nonceStr' => $this->generateNonceStr(),
                    'package' => "prepay_id=" . $arr['prepay_id'],
                    'signType' => 'MD5'
                );

                $pay_info['paySign'] = $this->getSignature($pay_info);

                return $pay_info;
            }
        }

        return false;
    }
}